﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Data.Tracking;
using ScrumTable.DL.Data.Schema;
using ScrumTable.DL.Data.Schema.ScrumTableV1;

#endregion

namespace ScrumTable.DL.Data.Generic.Cache
{
    /// <summary>
    /// This class contains all cached data elements.  All data
    /// mutations of should be executed on the current class.
    /// </summary>
    public class CacheContext : CacheElementBase
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private string _currentProjectUniqueId = CacheIdentity.DefaultUniqueId;
        private readonly CacheElementFactory _elementFactory = new CacheElementFactory();

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the top context instance.
        /// </summary>
        public override CacheContext OwnerContext { get { return this; } }

        /// <summary>
        /// Gets the owning project instance.
        /// </summary>
        public override CacheProject Project { get { return null; } }

        /// <summary>
        /// Gets the associated schema instance.
        /// </summary>
        public ScrumTableV1Schema Schema { get; private set; }

        /// <summary>
        /// Gets a dictionary of projects.
        /// </summary>
        /// <remarks>Returns the retrieved project collection or a null reference.</remarks>
        public CacheElementList<CacheProject> Projects { get; private set; }

        /// <summary>
        /// Gets the associated element factory instance, which creates
        /// the new elements for the current context.
        /// </summary>
        public CacheElementFactory ElementFactory
        {
            get { return _elementFactory; }
        }

        /// <summary>
        /// Gets a flag which determines that the current CacheContext is in
        /// Merge mode. This means, that changes from the back end system
        /// are imported into the current cache.
        /// </summary>
        public bool IsInMergeProcess { get; private set; }

        /// <summary>
        /// Gets the current (imported) project instance.
        /// </summary>
        public CacheProject CurrentProject
        {
            get
            {
                if (_currentProjectUniqueId == CacheIdentity.DefaultUniqueId)
                {
                    return (from cacheProject in Projects
                            where cacheProject.IsDataImported
                            select cacheProject).FirstOrDefault();
                }
                else
                {
                    return Projects[_currentProjectUniqueId];
                }
            }
            set
            {
                PreCondition.AssertNotNull(value, "value");

                _currentProjectUniqueId = value.UniqueId;
            }
        }

        /// <summary>
        /// Gets a flag which indicates that value change events on the
        /// current context should be propagated to the event listeners.
        /// </summary>
        internal bool PropagateEvents { get; set; }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheContext"/> class.
        /// </summary>
        /// <param name="schema">Specifies the underlying scrum table schema.</param>
        public CacheContext(ScrumTableV1Schema schema)
            : base("Context", CacheIdentity.DefaultId, CacheIdentity.DefaultUniqueId, CacheIdentity.DefaultRevision)
        {
            PreCondition.AssertNotNull(schema, "schema");

            Schema = schema;
            PropagateEvents = false;
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Enables the event propagation which allows the data layer to
        /// fire events to the layers above (DM).
        /// </summary>
        /// <param name="toExecuteAfterReset">Provides an action which is called after the propagation event has been reset.</param>
        /// <returns>Returns a disposable instance which must be used to reset the propagation state.</returns>
        public IDisposable EnableEventPropagation(Action toExecuteAfterReset = null)
        {
            bool valueBefore = PropagateEvents;
            PropagateEvents = true;

            return new DisposeAction(
                () =>
                {
                    PropagateEvents = valueBefore;

                    if (toExecuteAfterReset != null)
                    {
                        toExecuteAfterReset();
                    }
                });
        }

        /// <summary>
        /// Imports the specified projects into the current context.
        /// </summary>
        /// <param name="projects">Specifies the initialized project instances.</param>
        public void Import(IEnumerable<CacheProject> projects)
        {
            PreCondition.AssertNotNull(projects, "projects");

            Projects = new CacheElementList<CacheProject>(projects, this);
            Projects.ForEach(p => p.Validate());
        }

        /// <summary>
        /// Gets the project and iterations with the specified ids.
        /// </summary>
        /// <param name="projectId">Unique id of the project to retrieve.</param>
        /// <param name="iterationId">Unique id of the iteration to retrieve.</param>
        /// <returns>Returns a pair of project/iteration objects.</returns>
        internal Pair<CacheProject, CacheIteration> GetProjectAndIteration(string projectId, string iterationId)
        {
            CacheIteration iteration = null;
            CacheProject project = Projects.TryGetById(projectId);

            if (project != null)
            {
                iteration = project.Iterations.TryGetById(iterationId);
            }
            return new Pair<CacheProject, CacheIteration>(project, iteration);
        }

        /// <summary>
        /// Checks the cache data structures for changes and lists them
        /// in the CacheDataChanges instance.
        /// </summary>
        /// <returns>Returns the changes as a new instance.</returns>
        internal CacheDataChanges GetCurrentChanges()
        {
            if (CurrentProject != null)
            {
                return CurrentProject.GetChanges();
            }
            return new CacheDataChanges();
        }

        /// <summary>
        /// Merges the specified changes with into the current project instance. 
        /// </summary>
        /// <param name="toAccept">Specifies the changed entries which have been processed.</param>
        internal void MergeCurrentChanges(CacheDataChanges toAccept)
        {
            IsInMergeProcess = true;

            try
            {
                if (CurrentProject != null && toAccept != null)
                {
                    CurrentProject.MergeChanges(toAccept);
                }
            }
            finally
            {
                IsInMergeProcess = false;
            }
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------
        #endregion

    }
}